package com.ruoyi.system.service.h5.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.UnifiedOrderModel;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.RequestContext;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.WebUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.domain.MiniActivationCode;
import com.ruoyi.system.domain.MiniOrder;
import com.ruoyi.system.domain.MiniPriceConfig;
import com.ruoyi.system.domain.MiniUser;
import com.ruoyi.system.domain.dto.ExchangeDTO;
import com.ruoyi.system.domain.dto.RechargeDTO;
import com.ruoyi.system.service.IMiniActivationCodeService;
import com.ruoyi.system.service.IMiniPriceConfigService;
import com.ruoyi.system.service.MiniUserService;
import com.ruoyi.system.service.h5.IRechargeService;
import com.ruoyi.system.service.h5.MiniOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author :liding
 * @date : 2023/11/14 11:29
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class RechargeServiceImpl implements IRechargeService {
    @Autowired
    private MiniOrderService miniOrderService;
    @Autowired
    private IMiniPriceConfigService miniPriceConfigService;
    @Autowired
    private MiniUserService miniUserService;
    @Autowired
    private IMiniActivationCodeService iMiniActivationCodeService;
    /**
     * 微信充值
     *
     * @param body
     * @return
     */
    @Override
    public AjaxResult recharge(RechargeDTO body) {
        String openId = body.getOpenId();
        Long productId = body.getProductId();
        MiniUser user = miniUserService.getOne(Wrappers.<MiniUser>lambdaQuery()
                .eq(MiniUser::getOpenid, openId));
        MiniPriceConfig product = miniPriceConfigService.getById(productId);
        if (user==null || product==null){
            return AjaxResult.error("用户信息不存在或者产品不存在");
        }
        String orderNo = IdUtils.generateOrderNo();
        MiniOrder order = initOrder(body, user, product, orderNo);
        boolean saveResult = miniOrderService.save(order);
        if (!saveResult){
            return AjaxResult.error("订单保存失败请联系管理员");
        }
        Map<String, String> params = UnifiedOrderModel.builder()
                .appid(Constants.APP_ID)
                .mch_id(Constants.MCH_ID)
                .nonce_str(WxPayKit.generateStr())
                .body("题材笔记-vip充值")
                .attach("")
                .out_trade_no(orderNo)
                .total_fee(String.valueOf(product.getDiscount().intValue()))
                .spbill_create_ip(WebUtils.getRemoteAddr(RequestContext.currentInstance().getRequest()))
                .notify_url("https://admin.ticaibiji.cn/stage-api/recharge/callback")
                .trade_type(TradeType.JSAPI.getTradeType())
                .openid(openId)
                .build()
                // 同时支持 SignType.MD5、SignType.HMACSHA256
                .createSign(Constants.MCH_SECRET, SignType.MD5);
        log.info("openId下单入参:"+JSON.toJSONString(params));
        // 发送请求
        String xmlResult = WxPayApi.pushOrder(false, params);
        log.info(xmlResult);
        // 将请求返回的 xml 数据转为 Map，方便后面逻辑获取数据
        Map<String, String> resultMap = WxPayKit.xmlToMap(xmlResult);
        // 判断返回的结果
        String returnCode = resultMap.get("return_code");
        String returnMsg = resultMap.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            return AjaxResult.error(returnMsg);
        }
        String resultCode = resultMap.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            return AjaxResult.error(returnMsg);
        }

        // 以下字段在return_code 和result_code都为SUCCESS的时候有返回
        String prepayId = resultMap.get("prepay_id");
        // 二次签名，构建公众号唤起支付的参数,这里的签名方式要与上面统一下单请求签名方式保持一致
        Map<String, String> packageParams = WxPayKit.prepayIdCreateSign(prepayId, Constants.APP_ID, Constants.MCH_SECRET, SignType.MD5);
        // 将二次签名构建的数据返回给前端并唤起公众号支付
        return AjaxResult.success(packageParams);
    }

    private MiniOrder initOrder(RechargeDTO body, MiniUser user, MiniPriceConfig product, String orderNo) {
        return new MiniOrder(null,
                orderNo,
                product.getId(),
                product.getName(),
                product.getDiscount(),
                null,
                user.getId(),
                user.getOpenid(),
                "0",
                user.getInviteUserId(),
                user.getInviteType(),
                Timestamp.valueOf(LocalDateTime.now()));
    }

    /**
     * 微信回调
     *
     * @param request
     * @return
     */
    @Override
    public String callback(HttpServletRequest request) {
        String xmlMsg = HttpKit.readData(request);
        log.info("支付通知=" + xmlMsg);
        Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
        log.info("支付通知map:"+JSON.toJSONString(params));
        //支付通知map:{"transaction_id":"4200002002202311291790446378","nonce_str":"006c26b13be84004a6a50ab95d4da975","bank_type":"OTHERS","openid":"o1DGW645QTbjSZq_dInHLaPt1lGc","sign":"B192057C9E45936B4B1D1E32656C0F6D","fee_type":"CNY","mch_id":"1627902055","cash_fee":"1","out_trade_no":"202311292135787165","appid":"wx9370dd17a82410af","total_fee":"1","trade_type":"JSAPI","result_code":"SUCCESS","time_end":"20231129210553","is_subscribe":"N","return_code":"SUCCESS"}

        String returnCode = params.get("return_code");

        // 注意重复通知的情况，同一订单号可能收到多次通知，请注意一定先判断订单状态
        String orderNo = params.get("out_trade_no");
        MiniOrder order = miniOrderService.getOne(Wrappers.<MiniOrder>lambdaQuery().eq(MiniOrder::getOrderNo, orderNo));
        if ("1".equals(order.getState())) {
            return null;
        }

        // 注意此处签名方式需与统一下单的签名类型一致
        if (WxPayKit.verifyNotify(params, Constants.MCH_SECRET, SignType.MD5)) {
            if (WxPayKit.codeIsOk(returnCode)) {
                // 更新订单信息
                MiniUser user = miniUserService.getById(order.getUserId());
                MiniPriceConfig product = miniPriceConfigService.getById(order.getProductId());
                LocalDateTime nowTime = LocalDateTime.now();
                MiniUser updateUser = new MiniUser();
                updateUser.setId(user.getId());
                updateUser.setVip("88");
                updateUser.setVipType(order.getProductId().toString());
                updateUser.setVipFlag("1");
                updateUser.setVipTime(rechargeVipTime(nowTime, user.getVipTime(), product.getDuration(), ChronoUnit.DAYS));
                miniUserService.updateById(updateUser);
                MiniOrder updateOrder = new MiniOrder();
                updateOrder.setId(order.getId());
                updateOrder.setState("1");
                updateOrder.setPayTime(Timestamp.valueOf(nowTime));
                miniOrderService.updateById(updateOrder);
                // 发送通知等
                Map<String, String> xml = new HashMap<String, String>(2);
                xml.put("return_code", "SUCCESS");
                xml.put("return_msg", "OK");
                return WxPayKit.toXml(xml);
            }
        }
        return null;
    }

    @Override
    public AjaxResult exchange(ExchangeDTO body) {
        if (StringUtils.isBlank(body.getCode())||StringUtils.isBlank(body.getOpenId())) {
            return AjaxResult.error("参数错误");
        }
        MiniUser user = miniUserService.getOne(Wrappers.<MiniUser>lambdaQuery().eq(MiniUser::getOpenid, body.getOpenId()));
        if (Objects.isNull(user)) {
            return AjaxResult.error("用户不存在");
        }
        MiniActivationCode code = iMiniActivationCodeService.getOne(Wrappers.<MiniActivationCode>lambdaQuery().eq(MiniActivationCode::getCode, body.getCode()).eq(MiniActivationCode::getStatus, '0'));
        if (Objects.isNull(code)){
            return AjaxResult.error("兑换码不存在");
        }
        if (code.getUsed()==1){
            return AjaxResult.error("兑换码已被使用");
        }
        MiniUser updateUser = new MiniUser();
        updateUser.setId(user.getId());
        updateUser.setVip("88");
        updateUser.setVipType(body.getCode());
        updateUser.setVipFlag("1");
        LocalDateTime nowTime = LocalDateTime.now();
        updateUser.setVipTime(rechargeVipTime(nowTime, user.getVipTime(), Math.toIntExact(code.getAmount() * code.getType()), ChronoUnit.DAYS));
        miniUserService.updateById(updateUser);

        iMiniActivationCodeService.updateById(new MiniActivationCode().setId(code.getId())
                .setOpenid(body.getOpenId())
                .setUsed(1));
        return AjaxResult.success();
    }

    private Timestamp rechargeVipTime(LocalDateTime nowTime, Timestamp oldTime, Integer count, ChronoUnit unit) {
        if (Objects.isNull(oldTime)){
            return Timestamp.valueOf(nowTime.plus(count, unit));
        }
        LocalDateTime oldTime1 = oldTime.toLocalDateTime();
        if (nowTime.isAfter(oldTime1)) {
            return Timestamp.valueOf(nowTime.plus(count, unit));
        } else {
            return Timestamp.valueOf(oldTime1.plus(count, unit));
        }
    }
}
